package main

import (
	"bufio"
	"code.google.com/p/goplan9/plan9"
	"code.google.com/p/goplan9/plan9/acme"
	"code.google.com/p/goplan9/plan9/client"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

func findWin(name string) (*acme.Win, error) {
	fsys, err := client.MountService("acme")
	if err != nil {
		return nil, err
	}

	fid, err := fsys.Open("index", plan9.OREAD)
	if err != nil {
		return nil, err
	}

	winId := -1
	reader := bufio.NewReaderSize(fid, 1024)
	for {
		line, isPrefix, err := reader.ReadLine()
		if err != nil {
			break
		}
		if isPrefix {
			return nil, errors.New("short buffer")
		}

		a := strings.Fields(string(line))
		if a[5] == name {
			winId, err = strconv.Atoi(a[0])
			if err != nil {
				return nil, err
			}
		}
	}

	if winId == -1 {
		return nil, nil
	}

	win, err := acme.Open(winId, nil)
	if err != nil {
		return nil, err
	}
	return win, nil
}

func main() {
	flag.Parse()
	if flag.NArg() > 1 {
		log.Fatal("bad invocation")
	}

	winidEnv := os.Getenv("winid")
	if winidEnv == "" {
		log.Fatal("no winid")
	}

	winid, err := strconv.Atoi(winidEnv)
	if err != nil {
		log.Fatal(err)
	}

	win, err := acme.Open(winid, nil)
	if err != nil {
		log.Fatal(err)
	}

	win.ReadAddr() // make sure addr is open
	win.Ctl("addr=dot")
	q0, q1, err := win.ReadAddr()
	if err != nil {
		log.Fatal(err)
	}

	// We read the actual file instead of data, because this
	// seems to kill acme on some files.
	win.Ctl("put")
	tag, err := win.ReadAll("tag")
	if err != nil {
		log.Fatal(err)
	}

	fields := strings.Fields(string(tag))
	if len(fields) < 1 {
		log.Fatal("missing fields in tag")
	}

	path := fields[0]
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}

	body := string(bytes)

	l0 := strings.Count(body[:q0], "\n")
	l1 := strings.Count(body[:q1], "\n")

	var addr string
	if l0 == l1 {
		addr = fmt.Sprintf("%s:%d\n", path, l0+1)
	} else {
		addr = fmt.Sprintf("%s:%d,%d\n", path, l0+1, l1+1)
	}

	if flag.NArg() == 1 {
		path = os.ExpandEnv(flag.Arg(0))
		path, err = filepath.Abs(path)
		if err != nil {
			log.Fatal(err)
		}
		win, err = findWin(path)
		if err != nil {
			log.Fatal(err)
		}
		if win == nil {
			log.Fatalf("no buffer %s", path)
		}
	} else {
		win, err = findWin("+Bookmarks")
		if err != nil {
			log.Fatal(err)
		}
		if win == nil {
			win, err = acme.New()
			if err != nil {
				log.Fatal(err)
			}
			win.Name("+Bookmarks")
		}
	}

	win.Addr("$")
	win.Write("data", []byte(addr))
	if flag.NArg() == 1 {
		win.Ctl("put")
	} else {
		win.Ctl("clean")
	}
}
